welo git mirrors
Commit 36c03f034bc3410be9e73b66d8f2302b2041b9dd
Parents : 8f24583
Author : welo | main nixos computer <empty@empty.com>
Date : 2026-07-07T22:52:31+01:00
started adding filtering ip support and split off udp_relay into two different functions.
Changes
11 files changed, 519 insertions(+), 347 deletions(-)
Diff
diff --git a/Cargo.lock b/Cargo.lock
index 3af6384..dddf119 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -691,9 +691,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rns-core"
-version = "0.1.6"
+version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae55bc0a08d6fcc3750bb0fef12f602feb0116b6e6f8d22a572a77bfe57b06eb"
+checksum = "60e6a35f1558c4c5df1a522c474839b12806987bfc52beff61578e65dafffc96"
dependencies = [
"log",
"rns-crypto",
@@ -701,9 +701,9 @@ dependencies = [
[[package]]
name = "rns-crypto"
-version = "0.1.4"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9972cbe06fe42874f176b68a0faeb20f647308ced666d6c092b82d210d4c76"
+checksum = "2e505ccfe10b76928ee794246aef765ee56230c6596861e2780aaac8962450ef"
dependencies = [
"aes",
"cbc",
@@ -715,9 +715,9 @@ dependencies = [
[[package]]
name = "rns-net"
-version = "0.5.2"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30e03b52f537c0952c983c6250e98872a80e8238ef447c3e55bf6f6ac5183e80"
+checksum = "e7a315863b29f2bd547334731fb626dd64f2709752dd9b501c76f7d8d47f4540"
dependencies = [
"bzip2",
"libc",
diff --git a/Cargo.toml b/Cargo.toml
index 9040ee8..7ece538 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ tokio = { version = "1.44", features = ["full"] }
fast-socks5 = "1.0"
hex = "0.4"
-rns-net = "0.5"
+rns-net = "0.5.10"
rns-core = "0.1"
rns-crypto = "0.1"
udp-stream = "0.0.12"
diff --git a/examples/server.rs b/examples/server.rs
index 73f7cca..d1aa40c 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -35,7 +35,8 @@ async fn main() {
eprintln!("Starting RNS SOCKS5 proxy server...");
eprintln!("Make sure rnsd is running (pip install rns && rnsd)");
eprintln!();
- rns_proxy::server::run_server(identity_file.as_deref()).await;
+ eprintln!("someone update this example latter xoxo")
+ // rns_proxy::server::run_server(identity_file.as_deref()).await;
}
_ => unreachable!(),
}
diff --git a/src/cli.rs b/src/cli.rs
index 7973eae..fe8d6d0 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -33,10 +33,18 @@ pub enum Commands {
#[arg(short, long, default_value = "127.0.0.1:1080")]
listen: String,
},
- Forward {
+ Connect {
#[arg(short, long)]
destination: String,
+ /// Local SOCKS5 listen address
+ #[arg(short, long)]
+ ports: Vec<String>,
+ },
+ Forward {
+ #[arg(long, value_name = "PATH")]
+ identity_file: Option<String>,
+
/// Local SOCKS5 listen address
#[arg(short, long)]
ports: Vec<String>,
diff --git a/src/client.rs b/src/client.rs
index 033ccff..69de6ad 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -22,17 +22,20 @@ use fast_socks5::server::states::{CommandRead, Opened};
use fast_socks5::util::target_addr::TargetAddr;
use fast_socks5::{ReplyError, Socks5Command};
use log::{debug, error, info, warn};
+use rns_net::discovery::filter_and_sort_interfaces;
use rns_net::{LinkId, RnsNode};
use tokio::net::{TcpListener, TcpStream, UdpSocket};
use tokio::sync::mpsc::UnboundedReceiver;
use tokio::sync::{mpsc, Notify};
use tokio::time::error::Elapsed;
-use crate::forwarding::ForwardedPortType::{self, Tcp};
+use crate::filter::filter_and_convert;
+use crate::forwarding::PortType::{self, Tcp};
use crate::forwarding::{ForwardedPort, tcp_tunnel, udp_tunnel};
use crate::mux::MuxHandle;
+use crate::relay::relay_bidirectional_udp_client_side;
use crate::{
- Frame, FrameType, ProxyEvent, create_node, encode_connect_payload, ensure_path, recall_sig_pub, relay_bidirectional_tcp, relay_bidirectional_udp
+ Frame, FrameType, ProxyEvent, create_node, encode_connect_payload, ensure_path, recall_sig_pub, relay_bidirectional_tcp
};
pub async fn run_client(server_hex: &str, listen_addr: &str) {
@@ -52,14 +55,15 @@ pub async fn run_client_forward(server_hex: &str, ports: Vec<ForwardedPort> ) {
for port in ports {
match port.r#type {
- ForwardedPortType::Tcp => {
+ PortType::Tcp => {
tcp_tunnel(mux.clone(), reconnect_notify.clone(), port).await
},
- ForwardedPortType::Udp => {
+ PortType::Udp => {
udp_tunnel(mux.clone(), reconnect_notify.clone(), port).await
},
- ForwardedPortType::TcpUdp => {
-
+ PortType::TcpUdp => {
+ tcp_tunnel(mux.clone(), reconnect_notify.clone(), port.clone()).await;
+ udp_tunnel(mux.clone(), reconnect_notify.clone(), port).await;
}
}
}
@@ -343,7 +347,7 @@ async fn handle_socks5_session(
}},
Socks5Command::UDPAssociate =>
{if let Some((udp_stream,stream)) = handle_udp_connect(sid, mux.clone(), &mut session_rx, proto, target_addr).await {
- relay_bidirectional_udp(sid, udp_stream, Some(stream), mux, session_rx, true).await;
+ relay_bidirectional_udp_client_side(sid, udp_stream, stream, mux, session_rx).await;
}},
Socks5Command::TCPBind => {_ = proto.reply_error(&ReplyError::CommandNotSupported).await;}
@@ -352,90 +356,91 @@ async fn handle_socks5_session(
}
-pub async fn udp_bind_connect(
+
+pub async fn connect_tcp_server_side(
sid: u32,
mux: MuxHandle,
session_rx: &mut mpsc::UnboundedReceiver<Frame>,
target_addr: TargetAddr,)
- -> Result<Result<(),String>,Elapsed>
+ -> Option<Result<(),String>>
{
- let (host, port) = target_addr.into_string_and_port();
-
- info!("[{}] -> {}:{}", sid, host, port);
-
- // Send CONNECT frame through RNS
- let connect_payload = encode_connect_payload(&host, port, true);
- mux.send(FrameType::Connect, sid, connect_payload);
-
- // Wait for CONN_OK or CONN_ERR with timeout
- tokio::time::timeout(Duration::from_secs(15), async {
- while let Some(frame) = session_rx.recv().await {
- match frame.frame_type {
- FrameType::ConnectOk => return Ok(()),
- FrameType::ConnectErr => {
- let reason = String::from_utf8_lossy(&frame.payload).to_string();
- return Err(reason);
+ if let Some(socket_addr) = filter_and_convert(target_addr, None).await {
+
+ // Send CONNECT frame through RNS
+ let connect_payload = encode_connect_payload(&format!("{}",socket_addr.ip()), socket_addr.port(),false);
+ mux.send(FrameType::Connect, sid, connect_payload);
+
+ // Wait for CONN_OK or CONN_ERR with timeout
+ tokio::time::timeout(Duration::from_secs(15), async {
+ while let Some(frame) = session_rx.recv().await {
+ match frame.frame_type {
+ FrameType::ConnectOk => return Ok(()),
+ FrameType::ConnectErr => {
+ let reason = String::from_utf8_lossy(&frame.payload).to_string();
+ return Err(reason);
+ }
+ _ => continue,
}
- _ => continue,
}
- }
- Err("channel closed".to_string())
- })
- .await
+ Err("channel closed".to_string())
+ })
+ .await.ok()
+
+ } else {
+ return None;
+ }
}
-async fn handle_udp_connect(
+async fn handle_tcp_connect(
sid: u32,
mux: MuxHandle,
- mut session_rx: &mut mpsc::UnboundedReceiver<Frame>,
+ session_rx: &mut mpsc::UnboundedReceiver<Frame>,
proto: Socks5ServerProtocol<TcpStream,CommandRead>,
-
target_addr: TargetAddr,
-) -> Option<(UdpSocket, TcpStream)> {
+) -> Option<TcpStream> {
// info!("udp test data: {:?}, {:?}",cmd, target_addr);
- // Extract host and port from TargetAddr
-
- let connect_result = udp_bind_connect(sid,mux.clone(), &mut session_rx, target_addr).await;
- // Reply to SOCKS5 client based on RNS connection result
- let udp_stream = UdpSocket::bind(format!("0.0.0.0:0")).await.expect("unable to get udp socket");
- let relay_port = udp_stream.local_addr().unwrap().port();
- let relay_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), relay_port);
+ let connect_result = connect_tcp_server_side(sid,mux.clone(), session_rx, target_addr).await;
+ // Reply to SOCKS5 client based on RNS connection result
+ let dummy_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
- match connect_result {
- Ok(Ok(())) => {
+ let stream = match connect_result {
+ Some(Ok(())) => {
// Connection succeeded -- send SOCKS5 success reply
- match proto.reply_success(relay_address).await {
- Ok(s) => Some((udp_stream,s)),
+ match proto.reply_success(dummy_addr).await {
+ Ok(s) => s,
Err(e) => {
- debug!("[{}] Failed to send SOCKS5 reply: {:?}", sid, e);
+ debug!("[{}] Failed to send SOCKS5 reply: {}", sid, e);
mux.send(FrameType::Close, sid, Vec::new());
mux.drop_session(sid);
return None;
}
}
}
- Ok(Err(reason)) => {
+ Some(Err(reason)) => {
warn!("[{}] Remote connect failed: {}", sid, reason);
let _ = proto.reply_error(&ReplyError::GeneralFailure).await;
mux.drop_session(sid);
return None;
}
- Err(_) => {
+ None => {
warn!("[{}] Connect timeout", sid);
let _ = proto.reply_error(&ReplyError::TtlExpired).await;
mux.drop_session(sid);
return None;
}
- }
+ };
+ // Data relay (shared implementation)
+ // relay_bidirectional_tcp(sid, stream, mux, session_rx).await;
+ return Some(stream)
+}
-}
-pub async fn connect_tcp_server_side(
+pub async fn udp_bind_connect(
sid: u32,
mux: MuxHandle,
session_rx: &mut mpsc::UnboundedReceiver<Frame>,
@@ -445,9 +450,9 @@ pub async fn connect_tcp_server_side(
let (host, port) = target_addr.into_string_and_port();
info!("[{}] -> {}:{}", sid, host, port);
-
+
// Send CONNECT frame through RNS
- let connect_payload = encode_connect_payload(&host, port,false);
+ let connect_payload = encode_connect_payload(&host, port, true);
mux.send(FrameType::Connect, sid, connect_payload);
// Wait for CONN_OK or CONN_ERR with timeout
@@ -467,28 +472,33 @@ pub async fn connect_tcp_server_side(
.await
}
-async fn handle_tcp_connect(
+async fn handle_udp_connect(
sid: u32,
mux: MuxHandle,
- session_rx: &mut mpsc::UnboundedReceiver<Frame>,
+ mut session_rx: &mut mpsc::UnboundedReceiver<Frame>,
proto: Socks5ServerProtocol<TcpStream,CommandRead>,
+
target_addr: TargetAddr,
-) -> Option<TcpStream> {
+) -> Option<(UdpSocket, TcpStream)> {
// info!("udp test data: {:?}, {:?}",cmd, target_addr);
+ // Extract host and port from TargetAddr
-
- let connect_result = connect_tcp_server_side(sid,mux.clone(), session_rx, target_addr).await;
+ let connect_result = udp_bind_connect(sid,mux.clone(), &mut session_rx, target_addr).await;
// Reply to SOCKS5 client based on RNS connection result
- let dummy_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
- let stream = match connect_result {
+ let udp_stream = UdpSocket::bind(format!("0.0.0.0:0")).await.expect("unable to get udp socket");
+ let relay_port = udp_stream.local_addr().unwrap().port();
+ let relay_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), relay_port);
+
+
+ match connect_result {
Ok(Ok(())) => {
// Connection succeeded -- send SOCKS5 success reply
- match proto.reply_success(dummy_addr).await {
- Ok(s) => s,
+ match proto.reply_success(relay_address).await {
+ Ok(s) => Some((udp_stream,s)),
Err(e) => {
- debug!("[{}] Failed to send SOCKS5 reply: {}", sid, e);
+ debug!("[{}] Failed to send SOCKS5 reply: {:?}", sid, e);
mux.send(FrameType::Close, sid, Vec::new());
mux.drop_session(sid);
return None;
@@ -507,14 +517,11 @@ async fn handle_tcp_connect(
mux.drop_session(sid);
return None;
}
- };
+ }
+
- // Data relay (shared implementation)
- // relay_bidirectional_tcp(sid, stream, mux, session_rx).await;
- return Some(stream)
-}
-/// Wait for a path to the server, then recall the identity and return sig_pub_bytes.
+}/// Wait for a path to the server, then recall the identity and return sig_pub_bytes.
async fn wait_for_path(node: &RnsNode, dest_hash: &[u8; 16]) -> [u8; 32] {
ensure_path(node, dest_hash, 30).await;
diff --git a/src/filter.rs b/src/filter.rs
index e3ec4bd..b759a2f 100644
--- a/src/filter.rs
+++ b/src/filter.rs
@@ -1,3 +1,10 @@
+use std::net::{IpAddr::{self, V4, V6}, Ipv4Addr, SocketAddr};
+
+use fast_socks5::util::target_addr::TargetAddr;
+use log::warn;
+
+use crate::{filter::{AddressFilter::{Address, Localhost, Private}, PortFilterType::Single}, forwarding::PortType, };
+
///! Socket Filter
///!
///! This allows the server side client to restrict what ips are allowed. as well as what ports
@@ -22,6 +29,142 @@
///! Localhost Exclude
///! Localhost:80:tcp
///!
-struct a {
+
+
+pub trait FilterSocket {
+ fn filter(&self, addr: &SocketAddr) -> bool;
+}
+
+#[derive(Clone)]
+pub enum FilterResult {
+ Exclude,
+ Include,
+} // filters can also do neither if they are not applicable.
+
+
+#[derive(Clone)]
+pub enum AddressFilter {
+ All,
+ Address(IpAddr),
+ Private,
+ Localhost,
+}
+impl FilterSocket for AddressFilter {
+ fn filter(&self, addr: &SocketAddr) -> bool {
+ match *self {
+ AddressFilter::All => true,
+ Address(filter) => addr.ip() == filter,
+ Private => match addr.ip() {
+ V4(addr) => addr.is_private(),
+ V6(addr) => addr.is_unique_local(),
+ },
+ Localhost => match addr.ip() {
+ V4(addr) => addr == Ipv4Addr::LOCALHOST, // loopback may not technically be localhost
+ // in ipv4 cause it can be any of 16 million addressses.
+ V6(addr) => addr.is_loopback(),
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum PortFilterType {
+ Single(u16),
+ All,
+}
+
+#[derive(Clone)]
+pub struct PortFilter {
+ pub port_filter: PortFilterType,
+ pub port_type: PortType,
+}
+
+impl FilterSocket for PortFilter {
+ fn filter(&self, addr: &SocketAddr) -> bool {
+ match self.port_filter {
+ Single(checking_port) => {
+ addr.port() == checking_port
+ },
+ PortFilterType::All => true,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct Filter {
+ pub address_filter: AddressFilter,
+ pub port_filter: PortFilter,
+ pub filter_result: FilterResult,
+}
+
+#[derive(Clone)]
+pub struct FilterConfig {
+ pub filters: Vec<Filter>, // last filter has the most priority.
+}
+
+/// Note that this functionality could potentially be used to expose the real ip address of the server
+/// by making the reticulum server do a bogus dns request and using that a marker to find where it came from.
+/// Unlikey but possible and filtering by everything wouldn't prevent this. as we perform filtering
+/// after having having resolved the real ip. Adding a "deny domainname" to FilterConfig may make sense
+/// to reduce the possible attack surface
+///
+/// Something to keep in mind.
+
+
+pub async fn target_to_socket(addr: TargetAddr) -> Option<SocketAddr> {
+ match addr.resolve_dns().await {
+ Ok(socket_packed) => { // idk why this function signature is like this.
+ if let TargetAddr::Ip(socket) = socket_packed {
+ Some(socket)
+ } else {
+ // this can never happen
+ warn!("this did happen");
+ return None;
+ }
+ }
+ Err(_) => {
+ return None;
+ }
+
+ }
+}
+
+
+pub async fn allowed_ip(socket_addr: SocketAddr, filter_config: &FilterConfig) -> bool {
+ let mut include = false; // by default we don't allow anything.
+
+ for filter in &filter_config.filters {
+ if filter.address_filter.filter(&socket_addr) && filter.port_filter.filter(&socket_addr) {
+ match filter.filter_result {
+ FilterResult::Exclude => {include = false}
+ FilterResult::Include => {include = true}
+ }
+ }
+ }
+
+ include
+}
+
+/// note that we are converting from TargetAddr to SocketAddr as well as filtering
+/// the difference being that TargetAddr can also be a domain name and port number
+/// instead of an ip address and port number. Most functions require ip rather than
+/// domain name and resolving dns and then using it to connect makes everything a
+/// this means that if the user has put a hostname into the socks proxy we are actually
+/// getting the real ip and then passing it back. This is prevent some sneaky stuff from happening.
+///
+/// also we don't currently discriminate against tcp or udp. that's a todo for later
+pub async fn filter_and_convert(addr: TargetAddr, filter_config: Option<&FilterConfig>) -> Option<SocketAddr> {
+ let socket_addr = target_to_socket(addr).await?;
+ if let Some(filter_config) = filter_config {
+ if allowed_ip(socket_addr, filter_config).await {
+ return Some(socket_addr)
+ } else {
+ return None
+ }
+
+ } else {
+ Some(socket_addr)
+ }
}
+
diff --git a/src/forwarding.rs b/src/forwarding.rs
index ff0f5b7..1cb048c 100644
--- a/src/forwarding.rs
+++ b/src/forwarding.rs
@@ -27,21 +27,31 @@ use udp_stream::UdpListener;
use crate::{client::{connect_tcp_server_side, udp_bind_connect}, mux::MuxHandle, relay::{relay_forwarded_tcp, relay_forwarded_udp}};
-pub enum ForwardedPortType {
+#[derive(Clone)]
+pub enum PortType {
Tcp,
Udp,
TcpUdp, // both
}
+#[derive(Clone)]
pub struct ForwardedPort {
pub server_port: u16,
pub client_port: u16,
- pub r#type: ForwardedPortType
+ pub r#type: PortType
}
-pub async fn udp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: ForwardedPort) {
- let target_addr = SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), port.client_port);
- let listener = match UdpListener::bind(target_addr).await {
+/// basically:
+/// 1. open up tcp listener
+/// 2. every type a program like a webpage tries to connect to the local socket
+/// it must be from a different tcp port. So we know what program is what based on that.
+/// so we send a connect request to the reticulm server socks server a CONNECT request
+/// 3. any time a new tcp packet arrives then add the destination localhost port of the reticulum server
+/// and any time we receive data we figure out which stream it corresponds to and send it back
+///
+///
+pub async fn tcp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: ForwardedPort) {
+ let listener = match TcpListener::bind(format!("127.0.0.1:{}",port.client_port)).await {
Ok(l) => l,
Err(e) => {
error!("Failed to local port at {}", port.client_port);
@@ -71,13 +81,12 @@ pub async fn udp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: Fo
let mut session_rx = mux.register_session(sid);
let mux_clone = mux.clone();
- // let connect_result = udp_bind_connect(sid,mux.clone(), &mut session_rx, target_addr).await;
-
- let target_addr = TargetAddr::Ip(SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), port.server_port ));
+ println!("{:?} {:?}", sid, addr);
+ let target_addr = TargetAddr::Ip(SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 0)); // ask for any port client server side
tokio::spawn(async move {
- if let Ok(_) = udp_bind_connect(sid, mux.clone(), &mut session_rx, target_addr ).await {
- relay_forwarded_udp(sid, stream, mux_clone, session_rx, port.server_port).await;
+ if let Some(_) = connect_tcp_server_side(sid, mux.clone(), &mut session_rx, target_addr ).await {
+ relay_forwarded_tcp(sid, stream, mux_clone, session_rx).await;
}
});
}
@@ -90,126 +99,15 @@ pub async fn udp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: Fo
}
}
-/// 1. open up udp listener
-/// 2. have a mutex list of all the udps associated with each server and client ports
-/// when a new udp port fires to the localhost port, UDP associate to get a corresponding
-/// udp port server side.
-/// 3. When you get a new udp packet from the socket, find the associated sid and mux
-/// from the mutex list.
-/// 4. when we receive a packet we should know based on the sid and mux which udp localhost
-/// port that is associated with and we can just pass it through.
-///
-///
-/// This implementation is very weird compared to the others cause we can't seperate every udp into
-/// different streams and unlike the standard socksv5 implementation we don't have a seperate relay port
-/// for every program trying to use udp, so the pattern that works for rest of the functions doesn't work
-/// so instead we just handle everything in one function and don't use tokio:spawn which means it's
-/// single threaded and a bit more inefficient.
-///
-/// Note, we don't bother with reconnect_notify cause udp is lossy anyways. If it doesn't get through
-/// then too bad. better to ignore it than to unbind the udp socket or something.
-/// the only other alternative is buffering the udp but I'm not bothering, the application layer
-/// can deal with 100% packet loss for like 5 seconds probs.
-///
-/// Note 2, we have no way of telling the server we are done with udp directly as it's connectionless but we
+/// Note 1, we have no way of telling the server we are done with udp directly as it's connectionless but we
/// could therotically ask the OS if that localhost udp socket we are receiving from is still open and figure
/// out if we can safely tell the server to delete that port in the lsit. Cause right now they just stay
/// till we disconnect the RNS link or we end the program, so we therotically could run out of
/// udp ports server side. Not really that much of a concern cause we should have a server side
/// limit anyways.
-// pub async fn udp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: ForwardedPort) {
-
-// let listener = match UdpSocket::bind(format!("127.0.0.1:{}",port.client_port)).await {
-// Ok(l) => l,
-// Err(e) => {
-// error!("Failed to local udp port at {}", port.client_port);
-// return;
-// }
-// };
-
-
-// let mut port_mapping: Vec<(u32,u16, UnboundedSender<Vec<u8>>)> = Vec::new();
-// // sid, local port number, sender from localhost udp to rns.
-
-// loop {
-// let mut buf = [0u8 ;65536]; // probably inefficient?
-// let accept_result = listener.recv_from(&mut buf).await;
-// let (size, addr) = match accept_result {
-// Ok(sa) => sa,
-// Err(e) => {
-// continue;
-// }
-// };
-// // let data = &(buf[..size]);
-// let associated_port = port_mapping.iter().find(|p| {p.1 == addr.port()} );
-// match associated_port {
-// Some((sid, client_port, sender)) => { // either it already exists in the mapping
-// let mut data = vec![];
-// data.extend_from_slice(&buf[..size]);
-// println!("{:?}", sender.send(data));
-// },
-// None => {
-// // let
-// let (sender, receiver) = unbounded_channel();
-// //
-// // put the thing into port_mapping;
-// port_mapping.push((0,addr.port(), sender));
-
-
-// if !mux.is_connected() {
-// warn!("No RNS UDP link, rejecting connection");
-// continue;
-// }
-
-// let sid = mux.next_session_id();
-// let session_rx = mux.register_session(sid);
-// let mux_clone = mux.clone();
-
-// tokio::spawn(async move {
-// handle_socks5_session(sid, stream, mux_clone, session_rx).await;
-// });
-// },
-// };
-
-
-
-
-// // let mux = reference.clone();
-// // if !mux.is_connected() {
-// // drop(stream);
-// // continue;
-// // }
-
-// // let sid = mux.next_session_id();
-// // let mut session_rx = mux.register_session(sid);
-// // let mux_clone = mux.clone();
-
-// // println!("{:?} {:?}", sid, addr);
-// // let target_addr = TargetAddr::Ip(SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), port.server_port ));
-
-// // tokio::spawn(async move {
-// // if let Ok(_) = connect_tcp_server_side(sid, mux.clone(), &mut session_rx, target_addr ).await {
-// // relay_forwarded_tcp(sid, stream, mux_clone, session_rx).await;
-// // }
-// // });
-
-// }
-// // note, this implementation doesn't actually do this. We assign one pory server side and that's
-// // it and the implementation only works when a single program is using the udp port. Otherwise
-// // we have to create a new udp socket server side for every single application
-// }
-
-/// basically:
-/// 1. open up tcp listener
-/// 2. every type a program like a webpage tries to connect to the local socket
-/// it must be from a different tcp port. So we know what program is what based on that.
-/// so we send a connect request to the reticulm server socks server a CONNECT request
-/// 3. any time a new tcp packet arrives then add the destination localhost port of the reticulum server
-/// and any time we receive data we figure out which stream it corresponds to and send it back
-///
-///
-pub async fn tcp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: ForwardedPort) {
- let listener = match TcpListener::bind(format!("127.0.0.1:{}",port.client_port)).await {
+pub async fn udp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: ForwardedPort) {
+ let target_addr = SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), port.client_port);
+ let listener = match UdpListener::bind(target_addr).await {
Ok(l) => l,
Err(e) => {
error!("Failed to local port at {}", port.client_port);
@@ -239,12 +137,13 @@ pub async fn tcp_tunnel(mux: MuxHandle, reconnect_notify: Arc<Notify> , port: Fo
let mut session_rx = mux.register_session(sid);
let mux_clone = mux.clone();
- println!("{:?} {:?}", sid, addr);
+ // let connect_result = udp_bind_connect(sid,mux.clone(), &mut session_rx, target_addr).await;
+
let target_addr = TargetAddr::Ip(SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), port.server_port ));
tokio::spawn(async move {
- if let Ok(_) = connect_tcp_server_side(sid, mux.clone(), &mut session_rx, target_addr ).await {
- relay_forwarded_tcp(sid, stream, mux_clone, session_rx).await;
+ if let Ok(_) = udp_bind_connect(sid, mux.clone(), &mut session_rx, target_addr ).await {
+ relay_forwarded_udp(sid, stream, mux_clone, session_rx, port.server_port).await;
}
});
}
diff --git a/src/lib.rs b/src/lib.rs
index 2a41f01..10e0358 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,7 +20,7 @@ pub mod filter;
// Re-export commonly used items so existing `use crate::*` still works.
pub use frame::{decode_connect_payload, encode_connect_payload, Frame, FrameType};
pub use node::{create_node, ProxyEvent};
-pub use relay::{relay_bidirectional_udp,relay_bidirectional_tcp};
+pub use relay::{relay_bidirectional_tcp};
use log::info;
use rns_net::{DestHash, RnsNode};
diff --git a/src/main.rs b/src/main.rs
index 9b21d90..f505abe 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@
//! Run as either a server (exit node) or client (local SOCKS5 proxy).
use clap::Parser;
-use rns_proxy::{cli::{Cli, Commands}, client::run_client_forward, forwarding::{ForwardedPort, ForwardedPortType}};
+use rns_proxy::{cli::{Cli, Commands}, client::run_client_forward, filter::{Filter, FilterConfig, FilterResult, PortFilter}, forwarding::{ForwardedPort, PortType}};
#[tokio::main]
async fn main() {
@@ -18,7 +18,20 @@ async fn main() {
match cli.command {
Commands::Server { identity_file } => {
- rns_proxy::server::run_server(identity_file.as_deref()).await;
+ rns_proxy::server::run_server(identity_file.as_deref(),
+
+ FilterConfig {
+ filters: vec![Filter {
+ address_filter: rns_proxy::filter::AddressFilter::All,
+ port_filter: PortFilter{
+ port_filter: rns_proxy::filter::PortFilterType::All,
+ port_type: PortType::TcpUdp,
+ },
+ filter_result: FilterResult::Include,
+ }],
+ }
+
+ ).await;
}
Commands::Client {
destination,
@@ -26,14 +39,35 @@ async fn main() {
} => {
rns_proxy::client::run_client(&destination, &listen).await;
}
- Commands::Forward { destination, ports} => {
+ Commands::Connect { destination, ports} => {
run_client_forward(&destination, vec![ForwardedPort{
- server_port: 44444,
- client_port: 44444,
- r#type: ForwardedPortType::Udp
+ server_port: 34197,
+ client_port: 34197,
+ r#type: PortType::Udp
}]).await;
// rns_proxy::server::run_server(identity_file.as_deref()).await;
}
+ Commands::Forward {identity_file, ports} => {
+ // run_client_forward(&destination, vec![ForwardedPort{
+ // server_port: 34197,
+ // client_port: 34197,
+ // r#type: ForwardedPortType::Udp
+
+ // }]).await;
+ // // rns_proxy::server::run_server(identity_file.as_deref()).await;
+ rns_proxy::server::run_server(identity_file.as_deref(),
+ FilterConfig {
+ filters: vec![Filter {
+ address_filter: rns_proxy::filter::AddressFilter::All,
+ port_filter: PortFilter{
+ port_filter: rns_proxy::filter::PortFilterType::All,
+ port_type: PortType::TcpUdp,
+ },
+ filter_result: FilterResult::Include,
+ }],
+ }).await;
+
+ }
}
}
diff --git a/src/relay.rs b/src/relay.rs
index 9d374c8..083ad7b 100644
--- a/src/relay.rs
+++ b/src/relay.rs
@@ -13,6 +13,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::sync::{Mutex, mpsc};
use udp_stream::UdpStream;
+use crate::filter::{FilterConfig, allowed_ip, filter_and_convert};
use crate::frame::{Frame, FrameType};
use crate::mux::MuxHandle;
@@ -75,28 +76,16 @@ pub async fn relay_bidirectional_tcp(
-/// udp must still have an associated tcp connection to detect when the connection is over.
-/// this does not apply to the server (as in rns server) as it detects that the frame is being closed
-/// udp doesn't have a connetcion so the server cannot detect that the remote server the client is connecting
-/// to is offline per say.
-///
-/// basically, the client can stop the udp connection either by the reticulum link breaking
-/// OR the process using the udp connection stops
-/// while the server only stops in the first scenario because the server cannot know if the remote
-/// server not responding is part of the protocol.
-pub async fn relay_bidirectional_udp(
+/// udp client side must be split off to due a lot of things
+
+pub async fn relay_bidirectional_udp_client_side(
sid: u32,
socket: tokio::net::UdpSocket,
- tcp_stream: Option<tokio::net::TcpStream>,
+ tcp_stream: tokio::net::TcpStream,
mux: MuxHandle,
mut session_rx: mpsc::UnboundedReceiver<Frame>,
- wrap_packets: bool // on the client side whatever application
- // that is using the socksv5 proxy will add a header for where the udp packet is meant
- // to go, so we don't have to add that in ourselves, however on the server side RNS, when
- // the server receives a packet from a remote destination, it must wrap the udp packet with
- // the original location where that packet came from so the client knows of that information.
- // that's on the UDP -> RNS side, on the other side it's reversed.
) {
+
let socket = Arc::new(socket);
let socket1 = socket.clone();
@@ -133,18 +122,120 @@ pub async fn relay_bidirectional_udp(
println!("sending udp to rns data {:?} {:?}", &buf[..n], addr);
println!("sending udp to rns data {:?}", String::from_utf8_lossy(&buf[..n]));
println!("sid: {:?} ", sid);
- if wrap_packets {
- println!("sending packet raw");
- let mut a = client_local_port_1.lock().await;
- *a = Some(addr.port());
+ let mut a = client_local_port_1.lock().await; *a = Some(addr.port());
- mux_fwd.send(FrameType::Data, sid, buf[..n].to_vec());
- } else {
+ mux_fwd.send(FrameType::Data, sid, buf[..n].to_vec());
+ }
+ Err(e) => {
+ debug!("[{}] UDP read error: {}", sid, e);
+ break;
+ }
+ }
+ }
+ });
+
+ // RNS -> UDP
+ let rns_to_udp = tokio::spawn(async move {
+ loop {
+ if let Some(frame) = session_rx.recv().await {
+ println!("killed");
+ println!("{:?}", frame);
+ match frame.frame_type {
+ FrameType::Data => {
+ let a = client_local_port_2.lock().await;
+ let value = *a;
+
+ if let Some(port) = value {
+ println!("port: {:?}", port);
+ if let Err(e) = socket1.send_to(&frame.payload, (Ipv4Addr::LOCALHOST,port)).await {
+ warn!("[{}] UDP write error: {}", sid, e);
+ break;
+ } else {
+ println!("sent packet")
+ };
+
+ } else {
+ warn!("UDP received but client side does not know of a port");
+ // break // shouldn't break because this might not be the client's fault
+ // maybe some random bot send a udp request to that port before the client
+ // could do anything, so we just leave it open.
+ }
+ }
+ FrameType::Close => {println!("frame closed {:?}", frame); break},
+ _ => {}
+ }
+ } else {
+ println!("ended socket stream?");
+ break;
+
+ };
+
+ }
+ println!("ended");
+ });
+
+ let (mut tcp_read, mut _tcp_write) = tcp_stream.into_split();
+ let break_connection_tcp_check = tokio::spawn(async move {
+ let mut buf = [0u8; 4096];
+ loop {
+ match tcp_read.read(&mut buf).await {
+ Ok(0) => {
+ debug!("tcp connectioned associated with udp died {:?}", sid);
+ break;
+ },
+ Ok(n) => {
+ warn!("client still sending tcp through udp port {:?} {:?}", sid, &buf[0..n])
+ }
+ Err(e) => {
+ debug!("[{}] TCP read error: {}", sid, e);
+ break;
+ }
+ }
+ }
+ });
+ tokio::select! {
+ _ = udp_to_rns => {println!("udp end")},
+ _ = rns_to_udp => {println!("rns end")},
+ _ = break_connection_tcp_check => {println!("tcp end")},
+ }
+
+ mux.send(FrameType::Close, sid, Vec::new());
+ mux.drop_session(sid);
+}
+pub async fn relay_bidirectional_udp_server_side(
+ sid: u32,
+ socket: tokio::net::UdpSocket,
+ mux: MuxHandle,
+ mut session_rx: mpsc::UnboundedReceiver<Frame>,
+ filter_config: FilterConfig,
+) {
+ let socket = Arc::new(socket);
+ let socket1 = socket.clone();
+
+ let mux_fwd = mux.clone();
+
+ // UDP -> RNS
+ let udp_to_rns = tokio::spawn(async move {
+ let mut buf = [0u8; 4096];
+ loop {
+ let stuff = socket.recv_from(&mut buf).await;
+ println!("certified stuff {:?}", stuff);
+ match stuff {
+ Ok((0,_)) => {println!("end for some reason"); break},
+ Ok((n,addr)) => {
+ println!("sending udp to rns data {:?} {:?}", &buf[..n], addr);
+ println!("sending udp to rns data {:?}", String::from_utf8_lossy(&buf[..n]));
+ println!("sid: {:?} ", sid);
+
+ // okay i cant be boethered add filter config thing here
+ if allowed_ip(addr, &filter_config).await {
+
let mut packet = new_udp_header(addr).expect("cannot wrap udp packet");
packet.extend_from_slice(&buf[..n]);
println!("sending with stuff {:?}", packet);
mux_fwd.send(FrameType::Data, sid, packet.to_vec());
-
+ } else {
+ warn!("packet came from illegal server location")
}
}
Err(e) => {
@@ -161,63 +252,38 @@ pub async fn relay_bidirectional_udp(
if let Some(frame) = session_rx.recv().await {
println!("killed");
println!("{:?}", frame);
- if wrap_packets {
- match frame.frame_type {
- FrameType::Data => {
- let a = client_local_port_2.lock().await;
- let value = *a;
-
- if let Some(port) = value {
- println!("port: {:?}", port);
- if let Err(e) = socket1.send_to(&frame.payload, (Ipv4Addr::LOCALHOST,port)).await {
- warn!("[{}] UDP write error: {}", sid, e);
- break;
- } else {
- println!("sent packet")
- };
-
- } else {
- warn!("UDP received but client side does not know of a port");
- // break // shouldn't break because this might not be the client's fault
- // maybe some random bot send a udp request to that port before the client
- // could do anything, so we just leave it open.
- }
- }
- FrameType::Close => {println!("frame closed {:?}", frame); break},
- _ => {}
- }
- } else {
- match frame.frame_type {
- FrameType::Data => {
- match parse_udp_request(&*frame.payload).await {
- Ok((frag,addr,data)) => {
- println!("sending rns to udp data {:?}:{:?}:{:?}", frag, addr, data);
- println!("sending rns to udp data {:?}", String::from_utf8_lossy(data));
- println!("sending from: {:?} to {:?}", socket1.local_addr(), addr);
-
- let target = addr.into_string_and_port(); // string conversion is the only way to convert
- println!("{:?}", target);
- // between the tokio and fastsocksv5 versions for some reason
-
- if let Err(e) = socket1.send_to(data,target).await {
+ match frame.frame_type {
+ FrameType::Data => {
+ match parse_udp_request(&*frame.payload).await {
+ Ok((frag,addr,data)) => {
+ println!("sending rns to udp data {:?}:{:?}:{:?}", frag, addr, data);
+ println!("sending rns to udp data {:?}", String::from_utf8_lossy(data));
+ println!("sending from: {:?} to {:?}", socket1.local_addr(), addr);
+
+ if let Some(socket) = filter_and_convert(addr.clone(), Some(&filter_config)).await {
+
+ if let Err(e) = socket1.send_to(data,socket).await {
warn!("[{}] UDP write error: {}", sid, e);
break;
} else {
println!("sent packet")
}
- }
- Err(e) => {
- debug!("[{}] UDP read error: {}", sid, e);
- break
-
+ } else {
+ warn!("[{}] client attempted to send to illegal location {}",sid, addr)
}
- };
+ }
+ Err(e) => {
+ debug!("[{}] UDP read error: {}", sid, e);
+ break
+
+ }
+
+ };
- }
- FrameType::Close => {println!("frame closed {:?}", frame); break},
- _ => {}
}
+ FrameType::Close => {println!("frame closed {:?}", frame); break},
+ _ => {}
}
} else {
println!("ended socket stream?");
@@ -230,45 +296,25 @@ pub async fn relay_bidirectional_udp(
});
- let break_connection_tcp_check = match tcp_stream {
- Some(tcp_stream) => {
- let (mut tcp_read, mut tcp_write) = tcp_stream.into_split();
-
- tokio::spawn(async move {
- let mut buf = [0u8; 4096];
- loop {
- match tcp_read.read(&mut buf).await {
- Ok(0) => {
- debug!("tcp connectioned associated with udp died {:?}", sid);
- break;
- },
- Ok(n) => {
- warn!("client still sending tcp through udp port {:?} {:?}", sid, &buf[0..n])
- }
- Err(e) => {
- debug!("[{}] TCP read error: {}", sid, e);
- break;
- }
- }
- }
- })
- }
- None => {
- tokio::spawn(async move {
- tokio::time::sleep(Duration::from_hours(100000000000)).await; // lmao // I can't be bothered importing the empty future thing.
- })
- }
- };
tokio::select! {
_ = udp_to_rns => {println!("udp end")},
_ = rns_to_udp => {println!("rns end")},
- _ = break_connection_tcp_check => {println!("tcp end")},
}
mux.send(FrameType::Close, sid, Vec::new());
mux.drop_session(sid);
}
+/// udp must still have an associated tcp connection to detect when the connection is over.
+/// this does not apply to the server (as in rns server) as it detects that the frame is being closed
+/// udp doesn't have a connetcion so the server cannot detect that the remote server the client is connecting
+/// to is offline per say.
+///
+/// basically, the client can stop the udp connection either by the reticulum link breaking
+/// OR the process using the udp connection stops
+/// while the server only stops in the first scenario because the server cannot know if the remote
+/// server not responding is part of the protocol.
+
pub async fn relay_forwarded_tcp(
@@ -362,12 +408,35 @@ pub async fn relay_forwarded_udp(
}
});
- // RNS -> TCP
+ // RNS -> UDP
let rns_to_tcp = tokio::spawn(async move {
while let Some(frame) = session_rx.recv().await {
match frame.frame_type {
FrameType::Data => {
println!("{:?}", &frame.payload);
+ match parse_udp_request(&*frame.payload).await {
+ Ok((frag,addr,data)) => {
+ println!("sending rns to udp data {:?}:{:?}:{:?}", frag, addr, data);
+ println!("sending rns to udp data {:?}", String::from_utf8_lossy(data));
+
+ let target = addr.into_string_and_port(); // string conversion is the only way to convert
+ println!("{:?}", target);
+ // between the tokio and fastsocksv5 versions for some reason
+
+ if let Err(e) = udp_write.write_all(data).await {
+ warn!("[{}] UDP write error: {}", sid, e);
+ break;
+ } else {
+ println!("sent packet")
+ }
+ }
+ Err(e) => {
+ debug!("[{}] UDP read error: {}", sid, e);
+ break
+
+ }
+
+ };
if let Err(e) = udp_write.write_all(&frame.payload).await {
warn!("[{}] TCP write error: {}", sid, e);
break;
diff --git a/src/server.rs b/src/server.rs
index 06a436f..a5f5b8f 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -13,6 +13,7 @@ use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::Duration;
+use fast_socks5::util::target_addr::TargetAddr;
use log::{error, info, warn};
use rns_crypto::identity::Identity;
use rns_net::storage;
@@ -20,9 +21,11 @@ use rns_net::{Destination, IdentityHash, LinkId};
use tokio::net::{TcpStream, UdpSocket};
use tokio::sync::mpsc;
+use crate::filter::{FilterConfig, filter_and_convert};
use crate::mux::MuxHandle;
+use crate::relay::relay_bidirectional_udp_server_side;
use crate::{
- create_node, decode_connect_payload, relay_bidirectional_tcp, relay_bidirectional_udp, Frame, FrameType, ProxyEvent,
+ create_node, decode_connect_payload, relay_bidirectional_tcp, Frame, FrameType, ProxyEvent,
APP_ASPECT, APP_NAME,
};
@@ -45,7 +48,7 @@ fn identity_file_path(override_path: Option<&str>) -> PathBuf {
///
/// `identity_path` overrides the default identity file location
/// (`~/.reticulum/rns_proxy_identity`).
-pub async fn run_server(identity_path: Option<&str>) {
+pub async fn run_server(identity_path: Option<&str>, filter_config: FilterConfig) {
let id_path = identity_file_path(identity_path);
let identity = if id_path.exists() {
@@ -166,16 +169,18 @@ pub async fn run_server(identity_path: Option<&str>) {
let sid = frame.session_id;
if let Some((host, port,udp)) = decode_connect_payload(&frame.payload) {
info!("[{}] -> {}:{} {}", sid, host, port, udp);
+ let addr = TargetAddr::Domain(host, port);
let session_rx = mux.register_session(sid);
let mux_clone = mux.clone();
+ let config = filter_config.clone();
if udp {
tokio::spawn(async move {
- handle_server_session_udp(sid, host, port, mux_clone, session_rx)
+ handle_server_session_udp(sid, addr , mux_clone, session_rx, config)
.await;
});
} else {
tokio::spawn(async move {
- handle_server_session_tcp(sid, host, port, mux_clone, session_rx)
+ handle_server_session_tcp(sid, addr, mux_clone, session_rx, config)
.await;
});
}
@@ -205,47 +210,53 @@ pub async fn run_server(identity_path: Option<&str>) {
/// Handle a single proxied TCP session on the server side.
async fn handle_server_session_tcp(
sid: u32,
- host: String,
- port: u16,
+ addr: TargetAddr,
mux: MuxHandle,
session_rx: mpsc::UnboundedReceiver<Frame>,
+ filter_config: FilterConfig
) {
- // Attempt TCP connection
- let stream = match TcpStream::connect(format!("{}:{}", host, port)).await {
- Ok(s) => s,
- Err(e) => {
- warn!("[{}] Connection failed: {}", sid, e);
- mux.send(FrameType::ConnectErr, sid, e.to_string().into_bytes());
- mux.drop_session(sid);
- return;
- }
- };
- // Signal success
- mux.send(FrameType::ConnectOk, sid, Vec::new());
+ if let Some(socket) = filter_and_convert(addr.clone(), Some(&filter_config)).await {
+ let stream = match TcpStream::connect(socket).await {
+ Ok(s) => s,
+ Err(e) => {
+ warn!("[{}] Connection failed: {}", sid, e);
+ mux.send(FrameType::ConnectErr, sid, e.to_string().into_bytes());
+ mux.drop_session(sid);
+ return;
+ }
+ };
- // Data relay (shared implementation)
- relay_bidirectional_tcp(sid, stream, mux, session_rx).await;
- info!("[{}] TCP Closed", sid);
+ // Signal success
+ mux.send(FrameType::ConnectOk, sid, Vec::new());
+
+ // Data relay (shared implementation)
+ relay_bidirectional_tcp(sid, stream, mux, session_rx).await;
+ info!("[{}] TCP Closed", sid);
+ } else {
+ warn!("[{}] invalid ip address: {:?}", sid, &addr);
+ mux.send(FrameType::ConnectErr, sid, "invalid ip address".to_string().into_bytes());
+ mux.drop_session(sid);
+ return;
+ }
+ // Attempt TCP connection
}
/// Handle a single proxied UDP session on the server side.
async fn handle_server_session_udp(
sid: u32,
- host: String,
- port: u16,
+ target_addr: TargetAddr,
mux: MuxHandle,
session_rx: mpsc::UnboundedReceiver<Frame>,
+ filter_config: FilterConfig
) {
// Attempt UDP "connection"
- // generally the udp socket will connect to 0.0.0.0:0 to allow to send and receive from
- // every port. We don't police which sockets are valid here.
+ // we ignore whatever the client sent us and just connect to 0.0.0.0:0
+ // we do the actual filtering in relay_bidirectional_udp
- // warn!("ignoring what the client wanted to connect as and just")
- println!("{}:{}",host,port);
- // let socket = match UdpSocket::bind(format!("{}:{}",host,port)).await {
- let socket = match UdpSocket::bind("127.0.0.1:0").await {
+ let socket = match UdpSocket::bind("0.0.0.0:0").await {
+ // let socket = match UdpSocket::bind("127.0.0.1:0").await {
Ok(s) => s,
Err(e) => {
warn!("[{}] udp bind failed 1: {}", sid, e);
@@ -261,7 +272,7 @@ async fn handle_server_session_udp(
mux.send(FrameType::ConnectOk, sid, Vec::new());
// Data relay (shared implementation)
- relay_bidirectional_udp(sid, socket, None, mux, session_rx, false).await;
+ relay_bidirectional_udp_server_side(sid, socket, mux, session_rx, filter_config).await;
info!("[{}] UDP Closed", sid);
}
Served by rngit 1.4.2 - Generated in 0.09s